import string
import win32con

char_ranges = [
    (string.ascii_lowercase, -32),
    (string.digits, 0),
    ("?><:[]\\", 128),
    (";", 127),
    ("=", 126),
    ("/.,", 144),
    ("`{}|", 96),
    ("_", 94),
    ("-+", 144),
    ("'", 183),
    ('"', 188),
    ("~", 66),
    ("!",16),
    ("#$%&", 18),
    ("()", 17),
]

key_name_to_code = {}
key_code_to_name = {}

_better_names = [
    ("esc", win32con.VK_ESCAPE),
    ("enter", win32con.VK_RETURN),
    ("pgup", win32con.VK_BACK),
    ("pgdn", win32con.VK_NEXT),
]
def _fillmap():
    # Pull the VK_names from win32con
    names = [entry for entry in win32con.__dict__ if entry.startswith("VK_")]
    for name in names:
        n = name[3:].lower()
        val = getattr(win32con, name)
        key_name_to_code[n] = val
        key_code_to_name[val] = n
    # Some better named we know about
    for name, code in _better_names:
        key_name_to_code[name] = code
        key_code_to_name[code] = name
    # And the char_ranges map above
    for chars, offset in char_ranges:
        for char in chars:
            key_name_to_code[char] = ord(char)+offset
            key_code_to_name[ord(char)+offset] = char

_fillmap()

def get_scan_code(chardesc):
    return key_name_to_code.get(chardesc.lower())

modifiers = {
    "alt" : win32con.LEFT_ALT_PRESSED | win32con.RIGHT_ALT_PRESSED, 
    "lalt" : win32con.LEFT_ALT_PRESSED, 
    "ralt" : win32con.RIGHT_ALT_PRESSED,
    "ctrl" : win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED,
    "ctl" : win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED,
    "control" : win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED,
    "lctrl" : win32con.LEFT_CTRL_PRESSED,
    "lctl" : win32con.LEFT_CTRL_PRESSED,
    "rctrl" : win32con.RIGHT_CTRL_PRESSED,
    "rctl" : win32con.RIGHT_CTRL_PRESSED,
    "shift" : win32con.SHIFT_PRESSED,
    "key" : 0, # ignore key tag.
}

def parse_key_name(name):
    name = name + "-" # Add a sentinal
    start = pos = 0
    max = len(name)
    flags = 0
    scancode = None
    while pos<max:
        if name[pos] in "+-":
            tok = name[start:pos].lower()
            mod = modifiers.get(tok)
            if mod is None:
                # Its a key name
                scancode = get_scan_code(tok)
            else:
                flags = flags | mod
            pos = pos + 1 # skip the sep
            start = pos
        pos = pos + 1
    return scancode, flags

_checks = [
    [ # Shift
    ("Shift", win32con.SHIFT_PRESSED),
    ],
    [ # Ctrl key
    ("Ctrl", win32con.LEFT_CTRL_PRESSED | win32con.RIGHT_CTRL_PRESSED),
    ("LCtrl", win32con.LEFT_CTRL_PRESSED),
    ("RCtrl", win32con.RIGHT_CTRL_PRESSED),
    ],
    [ # Alt key
    ("Alt", win32con.LEFT_ALT_PRESSED | win32con.RIGHT_ALT_PRESSED),
    ("LAlt", win32con.LEFT_ALT_PRESSED),
    ("RAlt", win32con.RIGHT_ALT_PRESSED),
    ],
]

def make_key_name(scancode, flags):
    # Check alt keys.
    flags_done = 0
    parts = []
    for moddata in _checks:
        for name, checkflag in moddata:
            if flags & checkflag:
                parts.append(name)
                flags_done = flags_done & checkflag
                break
    if flags_done & flags:
        parts.append(hex( flags & ~flags_done ) )
    # Now the key name.
    try:
        parts.append(key_code_to_name[scancode])
    except KeyError:
        parts.append( "<Unknown scan code %s>" % scancode )
    sep = "+"
    if sep in parts: sep = "-"
    return sep.join([p.capitalize() for p in parts])

def _psc(char):
    sc = get_scan_code(char)
    print "Char %s -> %d -> %s" % (repr(char), sc, key_code_to_name.get(sc))

def test1():
    for ch in """aA0/?[{}];:'"`~_-+=\\|,<.>/?""":
        _psc(ch)
    for code in ["Home", "End", "Left", "Right", "Up", "Down", "Menu", "Next"]:
        _psc(code)

def _pkn(n):
    scancode, flags = parse_key_name(n)
    print "%s -> %s,%s -> %s" % (n, scancode, flags, make_key_name(scancode, flags))

def test2():
    _pkn("ctrl+alt-shift+x")
    _pkn("ctrl-home")
    _pkn("Shift-+")
    _pkn("Shift--")
    _pkn("Shift+-")
    _pkn("Shift++")
    _pkn("LShift-+")
    _pkn("ctl+home")
    _pkn("ctl+enter")
    _pkn("alt+return")
    _pkn("Alt+/")
    _pkn("Alt+BadKeyName")
    _pkn("A")
    _pkn("(")
    _pkn("Ctrl+(")
    _pkn("{")
    _pkn("!")

if __name__=='__main__':
    test2()